using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using Castle.Core;
using Castle.Core.Interceptor;
using Castle.DynamicProxy;
using Orchard;
using Orchard.ContentManagement.Drivers;

namespace Szmyd.Infiltrator.Autofac
{
    public class CallInterceptor : IInterceptor
    {
        private readonly IProfilingManager _manager;
        private readonly IWorkContextAccessor _context;
        readonly ProxyGenerator _proxyBuilder = new ProxyGenerator();

        public CallInterceptor(IProfilingManager manager, IWorkContextAccessor context)
        {
            _manager = manager;
            _context = context;
        }

        public void Intercept(IInvocation invocation)
        {
            if (!_manager.IsStarted)
            {
                invocation.Proceed();
                return;
            }

            var context = _context.GetContext();
            if(context != null && context.HttpContext != null && context.HttpContext.Request.Path.StartsWith("/Admin/Infiltrator"))
            {
                invocation.Proceed();
                return;
            }

            var invocationData = new InvocationData
            {
                Invocation = invocation,
                //StackTrace = new StackTrace(1),
            };

            _manager.PushInvocation(invocationData);

            var watch = new Stopwatch();
            watch.Start();
            invocation.Proceed();
            watch.Stop();

            invocationData.Duration = watch.Elapsed;

            _manager.Register(invocationData);

            var retVal = invocation.ReturnValue;

            if (retVal == null || ProxyServices.IsDynamicProxy(retVal.GetType())) {
                return;
            }
            var contentShapeResult = retVal as ContentShapeResult;
            if (contentShapeResult != null) {
                invocation.ReturnValue = BuildContentShapeResultProxy(contentShapeResult);
                return;
            }

            var combinedResult = retVal as CombinedResult;
            if (combinedResult != null) {
                invocation.ReturnValue = BuildCombinedResultProxy(combinedResult);
            }
        }

        private object BuildContentShapeResultProxy(ContentShapeResult instance)
        {
            var shapeType = typeof(ContentShapeResult).GetField("_shapeType", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(instance);
            var prefix = typeof(ContentShapeResult).GetField("_prefix", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(instance);
            var shapeBuilder = typeof(ContentShapeResult).GetField("_shapeBuilder", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(instance);
            return _proxyBuilder.CreateClassProxy(
                typeof(ContentShapeResult),
                new IInterceptor[] { _context.GetContext().Resolve<CallInterceptor>() }, shapeType, prefix, shapeBuilder);
        }

        private object BuildCombinedResultProxy(CombinedResult instance)
        {
            var results = typeof(CombinedResult).GetField("_results", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(instance);
            var proxiedResults = ((IEnumerable<DriverResult>)results).Select(r =>
            {
                var contentShapeResult = r as ContentShapeResult;
                return (DriverResult)(contentShapeResult != null ? BuildContentShapeResultProxy(contentShapeResult) : r);
            });

            return _proxyBuilder.CreateClassProxy(
                typeof(CombinedResult),
                new IInterceptor[] { _context.GetContext().Resolve<CallInterceptor>() }, proxiedResults);
        }
    }
}